package com.surprise.db.dao;

import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.persistence.Entity;
import javax.persistence.Table;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SingleColumnRowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import com.google.common.collect.Maps;
import com.surprise.base.bo.IHasIdInterface;
import com.surprise.base.util.ReflectionUtils;

/**
 * #table# is tablename
 * @author nick
 * 2013-11-18
 */
@SuppressWarnings("unchecked")
public abstract class SimpleDAO<T extends IHasIdInterface> {
	@Autowired
	protected JdbcTemplate jdbcTemplate;
	protected NamedParameterJdbcTemplate namedParameterJdbcTemplate;

	protected Log log = LogFactory.getLog(getClass());
	protected Class<T> entityClass;
	protected String tablename = null;
	protected String tableSymbol = "#table#";

	public SimpleDAO() {
		this.entityClass = ReflectionUtils.getSuperClassGenricType(getClass());
		tablename = getTableName();		
	}
	
	@PostConstruct
	public void init() {
		namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
	}
	
	public T findById(Long id) {
		String sql = "select * from #table# where id = ?";
		List<T> ret = findBySql(sql, id);
		if(CollectionUtils.isEmpty(ret)) {
			return null;
		}
		return ret.get(0);
	}

	public List<T> findByIds(Collection<Long> ids) {
		if (CollectionUtils.isEmpty(ids)) {
			return Collections.EMPTY_LIST;
		}
		String sql = "select * from " + tablename + " where id in (:ids)";
		Map<String, Object> params = Maps.newHashMap();
		params.put("ids", ids);

		List<T> ret = (List<T>) namedParameterJdbcTemplate.query(sql, params,
				new BeanPropertyRowMapper<T>(entityClass));
		return ret;
	}

	public List<T> findBySql(String sql, Object... args) {
		if(sql.contains(tableSymbol)) {
			sql = sql.replaceAll(tableSymbol, tablename);
		}
		if (args == null) {
			return jdbcTemplate.query(sql, new BeanPropertyRowMapper<T>(
					entityClass));
		}
		return (List<T>) jdbcTemplate.query(sql, args,
				new BeanPropertyRowMapper<T>(entityClass));
	}
	
	public <T> T findSingleBySql(String sql, Object args[], Class<T> clazz) {
		if(sql.contains(tableSymbol)) {
			sql = sql.replaceAll(tableSymbol, tablename);
		}
		if(args == null) {
			return jdbcTemplate.queryForObject(sql, new SingleColumnRowMapper<T>(clazz));
		}
		return jdbcTemplate.queryForObject(sql, args, new SingleColumnRowMapper<T>(clazz));
	}
	
	public int deleteById(Long id) {
		String sql = "delete from " + tablename + " where id = ? ";
		return jdbcTemplate.update(sql, id);
	}

	public int deleteById(Collection<Long> ids) {
		if (CollectionUtils.isEmpty(ids)) {
			return 0;
		}
		String sql = "delete from " + tablename + " where id in (:ids)";
		Map<String, Object> params = Maps.newHashMap();
		params.put("ids", ids);
		return namedParameterJdbcTemplate.update(sql, params);
	}

	public Map<Long, T> findMapByIds(Collection<Long> ids) {
		Map<Long, T> ret = Maps.newHashMap();
		if (CollectionUtils.isEmpty(ids)) {
			return ret;
		}
		List<T> entities = findByIds(ids);
		for (T entity : entities) {
			ret.put(entity.getId(), entity);
		}
		return ret;
	}

	public List<T> findAll() {
		String sql = "select * from " + tablename;
		List<T> ret = (List<T>) jdbcTemplate.query(sql,
				new BeanPropertyRowMapper<T>(entityClass));
		return ret;
	}
	
	public T findOneBySql(String sql, Object... args) {
        List<T> list = findBySql(sql, args);
        if(CollectionUtils.isNotEmpty(list)) {
            return list.get(0);
        }
        return null;
    }
	
	public int findCountBySql(String sql, Object... args) {
        List<T> list = findBySql(sql, args);
        if(CollectionUtils.isNotEmpty(list)) {
            return list.size();
        }
        return 0;
    }

	/**
	 * 如果类没有Table标签表明，则报错
	 * 
	 * @return
	 */
	public final String getTableName() {
		if (tablename != null) {
			return tablename;
		}
		Annotation[] annotations = entityClass.getAnnotations();
		for (Annotation annotation : annotations) {
			if (annotation instanceof Table) {
				Table an = (Table) annotation;
				tablename = an.name();
				return tablename;
			}
			if (annotation instanceof Entity) {
			    Entity en = (Entity) annotation;
			    tablename = en.name();
			    return tablename;
			}
		}

		throw new UnsupportedOperationException("getTableName() in "
				+ entityClass.getCanonicalName());
	}
}
